#!/usr/bin/env python
# -*- coding: utf-8 -*-

__author__ = 'Michael Liao'

import os
import time
import uuid
import json
import random
import urllib
import urllib2
import hashlib
import logging

from framework import cache, next_id, handler, emptyobject, create_oauth_cookie, COOKIE_NAME, COOKIE_EXPIRES
from appkey import _CLIENT_ID, _CLIENT_SECRET, _REDIRECT_URI

import web

################################################################################
# private constants                                                            #
################################################################################

################################################################################
# private functions                                                            #
################################################################################

class KbRoot(object):

    def __init__(self, root):
        self.id = root.id
        self.customer_id = root.customer_id
        self.private = root.private
        self.title = root.title
        self.content = root.content
        self.creation_time = root.creation_time
        self.modified_time = root.modified_time
        self.version = root.version
        self.children = []

    def add(self, node):
        '''
        Add a new child recursively
        '''
        if self.id == node.parent_id:
            self.children.append(node)
            return True
        for c in self.children:
            if c.add(node):
                return True
        return False

class KbNode(object):

    def __init__(self, node):
        self.id = node.id
        self.customer_id = node.customer_id
        self.root_id = node.root_id
        self.parent_id = node.parent_id
        self.title = node.title
        self.content = node.content
        self.creation_time = node.creation_time
        self.modified_time = node.modified_time
        self.version = node.version
        self.children = []

    def add(self, child):
        '''
        Add a new child recursively.
        Return True if added, otherwise False.
        '''
        if self.id == child.parent_id:
            self.children.append(child)
            return True
        for c in self.children:
            if c.add(child):
                return True
        return False

def current_time():
    '''
    Return current time as int (in milliseconds).
    '''
    return int(time.time() * 1000)

def check_write(kbroot):
    if web.ctx.user is None:
        raise web.forbidden('not signin yet')
    customer_id = web.ctx.customer.id
    if kbroot.customer_id!=customer_id:
        raise web.forbidden('root not belong to current customer')
    if web.ctx.user.customer_id!=customer_id:
        raise web.forbidden('user not belong to current customer.')
    # TODO: check write...

def find_customer(domain):
    '''
    Find customer by domain.
    '''
    cs = list(web.ctx.db.query('select * from kbcustomer where domain=$domain', vars=dict(domain=domain)))
    if cs:
        return cs[0]
    return None

def load_root(root_id):
    rs = list(web.ctx.db.query('select * from kbroot where id=$id', vars=dict(id=root_id)))
    if rs:
        return rs[0]
    raise web.notfound('root id not found')

def load_roots(root_id=None):
    '''
    Load all roots if user has permission, or all public roots.

    Return roots when root_id is None.

    Return (roots, root) when root_id is not None.

    Raise 404 error if root_id is unavailable.
    '''
    show_all = web.ctx.user is not None and web.ctx.user.customer_id==web.ctx.customer.id
    sql = 'select * from kbroot order by display_order, id' if show_all else 'select * from kbroot where private=0 order by display_order, id'
    L = web.ctx.db.query(sql)
    roots = [KbRoot(n) for n in L]
    if root_id:
        root = None
        for r in roots:
            if r.id==root_id:
                if r.private and not show_all:
                    raise web.notfound('Root is private.')
                root = r
                break
        return roots, root
    return roots

def load_tree(kbroot):
    '''
    Load tree and fill children of kbroot.
    '''
    L = list(web.ctx.db.query('select * from kbnode where root_id=$id order by display_order, creation_time', vars=dict(id=kbroot.id)))
    old_list = [KbNode(n) for n in L]
    while True:
        new_list = old_list[:]
        for node in new_list:
            if kbroot.add(node):
                old_list.remove(node)
        if len(new_list)==len(old_list):
            break

def load_node(id, check_root_id=None):
    '''
    Load node by its id. If check_root_id is not None, also check the node before return.

    Returns: the node object.

    Raise 404 if failed in found or check.
    '''
    L = list(web.ctx.db.query('select * from kbnode where id=$id', vars=dict(id=id)))
    if L:
        node = KbNode(L[0])
        if check_root_id and check_root_id!=node.root_id:
            raise web.notfound('root id not match')
        return node
    raise web.notfound('id not found')

def encode_url(url, **kw):
    '''
    Encode url with parameters.
    '''
    query = encode_params(**kw)
    if url.find('?')==(-1):
        return '%s?%s' % (url, query)
    return '%s&%s' % (url, query)

def encode_params(**kw):
    '''
    Encode parameters.
    '''
    args = []
    for k, v in kw.iteritems():
        qv = v.encode('utf-8') if isinstance(v, unicode) else str(v)
        args.append('%s=%s' % (k, urllib.quote(qv)))
    return '&'.join(args)

def http_call(url, body=None):
    '''
    send an http request and return its response as json object.
    '''
    req = urllib2.Request(url, data=body)
    resp = urllib2.urlopen(req)
    s = resp.read()
    return json.loads(s, object_hook=_obj_hook)

def get_referer():
    referer = web.ctx.env.get('HTTP_REFERER', '/')
    if referer.find('auth_') != (-1):
        return '/'
    return referer

### unverified ###################

def _obj_hook(pairs):
    '''
    convert json object to python object.
    '''
    o = JsonObject()
    for k, v in pairs.iteritems():
        o[str(k)] = v
    return o

class JsonObject(dict):
    '''
    general json object that can bind any fields but also act as a dict.
    '''
    def __getattr__(self, attr):
        if attr in self:
            return self[attr]
        return u''

    def __setattr__(self, attr, value):
        self[attr] = value
